package com.ruoyi.weiriya.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.WMember;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.Arith;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.weiriya.domain.*;
import com.ruoyi.common.core.domain.entity.WVehicle;
import com.ruoyi.weiriya.domain.vo.DoorQrCodeVo;
import com.ruoyi.weiriya.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.weiriya.mapper.WMemberDepositRecordMapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 用户停车记录Service业务层处理
 * 
 * @author lynn
 * @date 2023-08-01
 */
@Service
public class WMemberDepositRecordServiceImpl extends ServiceImpl<WMemberDepositRecordMapper, WMemberDepositRecord> implements IWMemberDepositRecordService
{

    @Autowired
    private IWMemberService memberService;

    @Autowired
    private IWNonVipSettingService nonVipSettingService;

    @Autowired
    private IWVehicleService vehicleService;

    @Autowired
    private IWDoorService doorService;

    @Autowired
    private IWCompanyService companyService;

    @Autowired
    private IWMessageService messageService;

    @Autowired
    private IWSettingService settingService;

    @Autowired
    private WMemberDepositRecordMapper memberDepositRecordMapper;

    @Override
    public boolean saveWMemberDepositRecord(DoorQrCodeVo vo) {

        Integer type = vo.getType();

        WDoor door = doorService.getOne(new LambdaQueryWrapper<WDoor>()
                .eq(WDoor::getDeviceId, vo.getId()));
        if(StringUtils.isNull(door) || door.getStatus() == 2){
            throw new ServiceException("门禁不存在或已停用\n请联系管理员！");
        }
        WMember member = memberService.getById(SecurityUtils.getUserId());
        // 出门
        if(type == 2){
            // 查询当前用户是否存在未结束订单
            List<WMemberDepositRecord> depositRecords = this.list(new LambdaQueryWrapper<WMemberDepositRecord>()
                    .eq(WMemberDepositRecord::getMemberId, SecurityUtils.getUserId())
                    .eq(WMemberDepositRecord::getAreaId, door.getAreaId())
                    .eq(WMemberDepositRecord::getState, 1));

            if(depositRecords.size() < 1 && member.getVipType() == 4){
                throw new ServiceException("不存在进场记录！");
            }

            // 结束未停止计费的订单
            for (WMemberDepositRecord depositRecord : depositRecords) {
                updateWMemberDepositRecordById(depositRecord);
            }
            // 开门
//            openDoor(door);
            return true;
        }

        if(StringUtils.isBlank(member.getAreaId()) || member.getMemberType() == 3 || member.getVipType() == 4){
            member.setAreaId(door.getAreaId());
            memberService.updateById(member);
        }

        // 判断非ViP用户余额是否支持开门
        if(member.getMemberType() == 3 || member.getVipType() == 4 || !member.getAreaId().equals(door.getAreaId())) {
            nonVipSettingService.checkLowestAmount(member, door.getAreaId());
        }


        // 查询当前用户是否存在未结束订单
        List<WMemberDepositRecord> depositRecords = this.list(new LambdaQueryWrapper<WMemberDepositRecord>()
                .eq(WMemberDepositRecord::getMemberId, SecurityUtils.getUserId())
                .eq(WMemberDepositRecord::getState, 1));
        // 结束未停止计费的订单
        for (WMemberDepositRecord depositRecord : depositRecords) {
            updateWMemberDepositRecordById(depositRecord);
        }
        // TODO  此处跳过了实际开门
        // 开门
//        openDoor(door);

        // 获取车辆信息
        WVehicle vehicle = vehicleService.getOne(new LambdaQueryWrapper<WVehicle>()
                .eq(WVehicle::getMemberId, SecurityUtils.getUserId()));
        WMemberDepositRecord wMemberDepositRecord = new WMemberDepositRecord();

        if(member.getMemberType() != 3){
            wMemberDepositRecord.setVehicleType(vehicle.getVehicleNonChargeTypeId());
            wMemberDepositRecord.setVehicleId(vehicle.getId());
        }
        wMemberDepositRecord.setMemberId(member.getId());
        wMemberDepositRecord.setVipType(member.getVipType());
        wMemberDepositRecord.setAreaId(member.getAreaId());
        if(!member.getAreaId().equals(door.getAreaId())){
            wMemberDepositRecord.setAreaId(door.getAreaId());
        }
        wMemberDepositRecord.setState(1);
        wMemberDepositRecord.setStartTime(new Date());
        wMemberDepositRecord.setDoorId(door.getId());

        return this.save(wMemberDepositRecord);
    }

    private void openDoor(WDoor door) {
        String url = RuoYiConfig.getApiUrl() + "/api/door/remoteOpenById";
        String param = "doorId=" + door.getId()
                + "&access_token=" + RuoYiConfig.getAccessToken()
                + "&interval=10";
        try {
            String body = HttpUtil.sendPostReq(url, param, "utf-8", 1, null, null);
            JSONObject jsonObject = JSONObject.parseObject(body);
            String message = jsonObject.getString("message");
            if(!"success".equals(message)){
                throw new ServiceException("开门失败请联系管理员！");
            }
        } catch (IOException e) {
            e.printStackTrace ( );
        }
    }

    @Override
    public boolean updateWMemberDepositRecordById(WMemberDepositRecord wMemberDepositRecord) {
        // 获取记录信息
        WMemberDepositRecord depositRecord = this.getById(wMemberDepositRecord.getId());
        if(!depositRecord.getMemberId().equals(SecurityUtils.getUserId().toString()))
            return false;
        Date date = new Date();
        long minute = (date.getTime() - depositRecord.getStartTime().getTime()) / 60000;
        // 记录结束时间和总时长
        depositRecord.setEndTime(date);
        depositRecord.setTotalDuration(minute);

        // 计算价格
        WMember member = memberService.getById(depositRecord.getMemberId());
        Integer vipType = member.getVipType();
        if(StringUtils.isNull(vipType) || vipType < 1 || vipType > 4){
            member.setVipType(4);
            vipType = 4;
            memberService.updateById(member);
        }
        depositRecord.setVipType(vipType);

        // 是否是VIP
        if(vipType == 4 || !member.getAreaId().equals(depositRecord.getAreaId())){
            // 获取当前小区的停车价格
            WNonVipSetting nonVipSetting = nonVipSettingService.getWNonVipSettingByAreaId(depositRecord.getAreaId());
            // 充电每小时金额
            Double stopHourAmount = nonVipSetting.getStopHourAmount();
            // 每天金额
            Double stopDayAmount = nonVipSetting.getStopDayAmount();

            // TODO　计算费用（目前逻辑：超过半小时算一个小时，未超过半小时不计费）
            // 换算为小时
            long round = Math.round((double) minute / 60D);
            Double amount = 0D;
            if(round <= 24){
                double amountTemp = Arith.mul(round, stopHourAmount);
                if(Double.compare(amountTemp, stopDayAmount) > 0){
                    amount = stopDayAmount;
                } else {
                    amount = amountTemp;
                }
            } else {
                long l = round / 24;
                long l1 = round - (l * 24);
                double amountTemp = Arith.mul(l1, stopHourAmount);
                double residue;
                if(Double.compare(amountTemp, stopDayAmount) > 0){
                    residue = stopDayAmount;
                } else {
                    residue = amountTemp;
                }
                amount = (stopDayAmount * l) + residue;
            }

            depositRecord.setAmount(amount);

            // 修改余额
            Double wallet = member.getWallet();
            double result = wallet - amount;
            member.setWallet(result);

            // 获取押金警戒线
            WSetting wSetting = settingService.getById("PARKING_DEPOSIT_WARNING_LINE");
            JSONObject jsonObject = JSONObject.parseObject(wSetting.getSettingValue());
            Double deposit = Double.valueOf(jsonObject.getString("amount"));
            if(result < deposit){
                messageService.remind(member, deposit);
            }
            memberService.updateById(member);

        } else {
            // VIP 免费
            depositRecord.setAmount(0D);
        }
        depositRecord.setState(2);

        // 发送消息
        WMessage wMessage = new WMessage();
        wMessage.setMessageType(1);
        wMessage.setUserType(2);
        wMessage.setMemberId(depositRecord.getMemberId());
        wMessage.setAreaId(depositRecord.getAreaId());
        wMessage.setMessageRange(1);
        wMessage.setMessageTitle("停车消费记录");
        wMessage.setMessageContent("开始时间：" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, depositRecord.getStartTime()) + "</br>" +
                "结束时间：" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, depositRecord.getEndTime()) + "</br>" +
                "消费：" + depositRecord.getAmount());
        wMessage.setCreateTime(new Date());
        wMessage.setCreateBy("1");
        wMessage.setUpdateTime(new Date());
        wMessage.setUpdateBy("1");
        messageService.save(wMessage);

        return this.updateById(depositRecord);
    }

    @Override
    public List<WMemberDepositRecord> getList(WMemberDepositRecord wMemberDepositRecord) {
        QueryWrapper<WMemberDepositRecord> queryWrapper = wMemberDepositRecord.queryWrapper();
        if(SecurityUtils.getLoginUser().isAdministrators()){
            if(StringUtils.isNotBlank(SecurityUtils.getLoginUser().getUser().getCompanyId())) {
                List<String> areaIdListByUserId = companyService.getAreaIdList();
                if(areaIdListByUserId.size() > 0) {
                    queryWrapper.in("mdr.area_id", areaIdListByUserId);
                } else {
                    return new ArrayList<>();
                }
            }
        } else{
            queryWrapper.eq("mdr.member_id", SecurityUtils.getUserId());
        }
        return memberDepositRecordMapper.getList(queryWrapper);
    }

    @Override
    public WMemberDepositRecord getWMemberDepositRecordById(String id) {
        return memberDepositRecordMapper.getWMemberDepositRecordById(id);
    }

    @Override
    public void getDoorListTask() {
        // 获取所有门集合
        String url = RuoYiConfig.getApiUrl() + "/api/door/list";
        String param = "pageNo=1" +
                "&pageSize=100" +
                "&access_token=" + RuoYiConfig.getAccessToken();
        String body = HttpUtils.sendGet(url, param, Constants.UTF8, null);
        JSONObject jsonObject = JSONObject.parseObject(body);
        JSONArray data = jsonObject.getJSONArray("data");

        // 获取系统已存在的所有门
        List<WDoor> doorList = doorService.list();
        List<String> doorIds = new ArrayList<>();
        if(doorList.size() > 0) {
            doorIds = doorList.stream().map(WDoor::getId).collect(Collectors.toList());
        }

        List<WDoor> list = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            WDoor wDoor = new WDoor();
            JSONObject object = data.getJSONObject(i);
            wDoor.setId(object.getString("id"));
            wDoor.setName(object.getString("name"));
            wDoor.setDeviceId(object.getString("deviceId"));

            list.add(wDoor);

        }
        List<WDoor> newList = new ArrayList<>();
        for (WDoor wDoor : list) {
            if(doorIds.contains(wDoor.getId())){
                doorIds.remove(wDoor.getId());
                continue;
            }
            // 生成二维码
            DoorQrCodeVo vo = new DoorQrCodeVo();
            vo.setId(wDoor.getDeviceId());
            vo.setType(1);

            // 拼接进门路径
            String filePath = RuoYiConfig.getUploadPath();
            filePath = filePath + "/QRCode/" + wDoor.getName() + "-进门.jpg";
            QrCodeUtil.generate(JSONUtil.toJsonStr(vo), 300, 300, FileUtil.file(filePath));
            String inUrl = "/profile/upload/QRCode/" + wDoor.getName() + "-进门.jpg";
            wDoor.setInletUrl(inUrl);

            // 拼接出门路径
            filePath = RuoYiConfig.getUploadPath();
            filePath = filePath + "/QRCode/" + wDoor.getName() + "-出门.jpg";
            vo.setType(2);
            QrCodeUtil.generate(JSONUtil.toJsonStr(vo), 300, 300, FileUtil.file(filePath));
            String outUrl = "/profile/upload/QRCode/" + wDoor.getName() + "-出门.jpg";
            wDoor.setExportUrl(outUrl);
            wDoor.setCreateBy("1");
            wDoor.setCreateTime(new Date());
            wDoor.setUpdateBy("1");
            wDoor.setUpdateTime(new Date());
            newList.add(wDoor);
        }
        if(newList.size() > 0) {
            doorService.saveBatch(newList);
        }
        if(doorIds.size() > 0){
            doorService.update(new LambdaUpdateWrapper<WDoor>()
                    .set(WDoor::getStatus, 2)
                    .eq(WDoor::getStatus, 1)
                    .in(WDoor::getId, doorIds));
        }
    }

    @Override
    public void timeoutSettlementTask() {
        List<WMemberDepositRecord> list = this.list(new LambdaQueryWrapper<WMemberDepositRecord>()
                .eq(WMemberDepositRecord::getState, 1)
                .eq(WMemberDepositRecord::getVipType, 4)
                .le(WMemberDepositRecord::getStartTime, DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.addDays(new Date(), -1))));
        List<WMemberDepositRecord> depositRecordList = new ArrayList<>();
        List<WMember> changeMemberList = new ArrayList<>();
        for (WMemberDepositRecord record : list) {
            // 获取当前小区的停车价格
            WNonVipSetting nonVipSetting = nonVipSettingService.getWNonVipSettingByAreaId(record.getAreaId());
            // 每天金额
            Double stopDayAmount = nonVipSetting.getStopDayAmount();
            Date startTime = record.getStartTime();

            Long differenceDay = DateUtils.getDifferenceDay(startTime, new Date());

            Double withheldAmount = record.getWithheldAmount();
            Double nowAmount = stopDayAmount * differenceDay;

            if(withheldAmount >= nowAmount){
                continue;
            }
            // 新计算的金额大于已扣减的金额则再次扣减用户余额
            // 扣减用户余额
            WMember member = memberService.getById(record.getMemberId());
            Double wallet = member.getWallet();
            wallet = wallet - (nowAmount - withheldAmount);
            member.setWallet(wallet);
            // 从新记录已扣减金额
            record.setWithheldAmount(nowAmount);
            depositRecordList.add(record);
            changeMemberList.add(member);
        }
        this.updateBatchById(depositRecordList);
        memberService.updateBatchById(changeMemberList);
    }
}
